package org.xxpay.pay.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.stereotype.Service;
import org.xxpay.apibase.exception.ApiException;
import org.xxpay.apibase.service.IDCPaymentService;
import org.xxpay.apibase.vo.ApiPayOrder;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.domain.api.AbstractRes;
import org.xxpay.core.common.domain.api.ApiBuilder;
import org.xxpay.core.common.domain.api.PayQueryRes;
import org.xxpay.core.common.domain.api.PayRes;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.common.util.XXPayUtil;
import org.xxpay.core.common.vo.DBApplicationConfig;
import org.xxpay.core.common.vo.OrderCostFeeVO;
import org.xxpay.core.entity.PayInterfaceType;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.core.entity.PayPassageAccount;
import org.xxpay.pay.channel.PayConfig;
import org.xxpay.pay.channel.PaymentInterface;
import org.xxpay.pay.util.SpringUtil;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/** 本地动态通道 service **/
@Service
public class LocalDynamicChannelService {

    private static final MyLog logger = MyLog.getLog(LocalDynamicChannelService.class);

    @Autowired
    private PayConfig payConfig;
    @Autowired
    private CommonService commonService;

    /** 放置本地所有的 <接口类型名称 - URLClassLoader >  **/
    private static final Map<String, URLClassLoader> URL_CLASSLOADER_MAP = new ConcurrentHashMap<>();

    /** 加载所有的动态通道 **/
    public synchronized void loadAll(){

        String path = payConfig.getIfStoreLocalLoadjarPath();
        File file = new File(path);
        if(!file.isDirectory()) {
            logger.error("读取[动态通道]列表失败! [{}] is not dir ！", file.getAbsolutePath());
            return ;
        }

        PayInterfaceType queryCondition = new PayInterfaceType();
        queryCondition.setStatus(MchConstant.PUB_YES); queryCondition.setIfSourceType(MchConstant.IF_SOURCE_TYPE_STORE_JAR);
        List<PayInterfaceType> typeList = commonService.payInterfaceTypeService.selectAll(queryCondition);
        for (PayInterfaceType typeItem : typeList) {
            try {

                reloadJar(typeItem.getIfTypeCode());
                logger.info("加载动态通道[{}]", typeItem.getIfTypeCode());
            } catch (Exception e) {

                logger.error("加载动态通道[{}] 失败！", typeItem.getIfTypeCode(), e);
            }
        }
    }


    /** 重新加载jar包, 并返回 urlClassLoader **/
    public synchronized URLClassLoader reloadJar(String ifTypeCode) {

        try {
            //如果存在则删除
            if(URL_CLASSLOADER_MAP.get(ifTypeCode) != null){
                URL_CLASSLOADER_MAP.get(ifTypeCode).close();
            }

            File jar = new File(appendLocalJarPath(ifTypeCode));
            URL[] urls = new URL[]{jar.toURI().toURL()};
            URL_CLASSLOADER_MAP.put(ifTypeCode, new URLClassLoader(urls));


            //重新加载 bean  如果有，  如果未定义， 不影响其他bean
            try {getPaymentInterface(ifTypeCode, true); } catch (Exception e) {}
            try {getPayNotifyInterface(ifTypeCode, true); } catch (Exception e) {}


            return URL_CLASSLOADER_MAP.get(ifTypeCode);

        } catch (Exception e) {
            logger.error("加载动态[{}]通道失败！", ifTypeCode, e);
            throw ServiceException.build(RetEnum.RET_SERVICE_LOAD_DYNAMIC_CHANNEL_ERROR);
        }
    }


    /** 获取service, 如 : PaymentInterface   **/
    public Object getService(String ifTypeCode, String serviceName, boolean forceReload4DC){

        String innerBeanName = ifTypeCode + serviceName; //内置通道
        String dcBeanName = "DC_" + ifTypeCode + "_" + serviceName; //动态通道

        try {

            if(!forceReload4DC){ //不强制重载

                //包含该service 直接返回
                if(SpringUtil.getApplicationContext().containsBean(innerBeanName)){
                    return SpringUtil.getBean(innerBeanName);
                }
                if(SpringUtil.getApplicationContext().containsBean(dcBeanName)){
                    return SpringUtil.getBean(dcBeanName);
                }

            }

            //以下为 强制重载 或者 未查询到通道信息 的情况
            //重新加载 bean   先删除bean  --》 注册bean

            URLClassLoader classLoader = URL_CLASSLOADER_MAP.get(ifTypeCode);
            if(classLoader == null) classLoader = reloadJar(ifTypeCode);

            //类的全限定名
            String packageName = "org.xxpay.dc." + ifTypeCode +  "." + dcBeanName;

            Class<?> aClass = classLoader.loadClass(packageName);
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(aClass);
            GenericApplicationContext context = (GenericApplicationContext) SpringUtil.getApplicationContext();

            if(context.containsBean(dcBeanName)){ //删除bean
                context.removeBeanDefinition(dcBeanName);
            }

            //注册bean
            context.registerBeanDefinition(dcBeanName, beanDefinitionBuilder.getRawBeanDefinition());
            return context.getBean(dcBeanName);

        } catch (ServiceException e) {
            throw e;

        }catch (Exception e1) {
            logger.error("加载[innerBeanName={} | dcBeanName]异常", innerBeanName, dcBeanName, e1);
            return null;
        }

    }




    /** 转换对象 **/
    private AbstractRes convertRes(org.xxpay.apibase.vo.api.AbstractRes originRes){

        if(originRes instanceof org.xxpay.apibase.vo.api.PayRes){
            return JSONObject.parseObject(JSON.toJSONString(originRes), PayRes.class);
        }

        if(originRes instanceof org.xxpay.apibase.vo.api.PayQueryRes){
            return JSONObject.parseObject(JSON.toJSONString(originRes), PayQueryRes.class);
        }

        return null;
    }


    private JSONObject getPayParamJSON(Integer passageAccountId) {
        String payParam = "";
        PayPassageAccount payPassageAccount = commonService.payPassageAccountService.findById(passageAccountId);

        if(payPassageAccount == null) throw new ServiceException(RetEnum.RET_MGR_PAY_PASSAGE_ACCOUNT_NOT_EXIST);

        payParam = payPassageAccount.getParam();
        if(StringUtils.isBlank(payParam)) throw new ServiceException(RetEnum.RET_MGR_PAY_PASSAGE_ACCOUNT_NOT_EXIST); //仍然为空，说明配置有误

        return JSONObject.parseObject(payParam);
    }



    /**     ↓  ↓  ↓  ↓  ↓   对外提供的通用函数  ↓  ↓  ↓  ↓  ↓        **/

    //////////////////////////////////////
    /** 获取bean: PaymentInterface    **/
    public Object getPaymentInterface(String ifTypeCode){
       return getService(ifTypeCode,"PaymentService", false);
    }

    /** 方法重载   **/
    public Object getPaymentInterface(String ifTypeCode, boolean forceReload){
        return getService(ifTypeCode,"PaymentService", forceReload);
    }

    //////////////////////////////////////

    /** 获取bean: PayNotifyInterface   **/
    public Object getPayNotifyInterface(String ifTypeCode){
        return getService(ifTypeCode, "PayNotifyService", false);
    }

    /** 方法重载     **/
    public Object getPayNotifyInterface(String ifTypeCode, boolean forceReload){
        return getService(ifTypeCode, "PayNotifyService", forceReload);
    }

    //////////////////////////////////////



    /** 拼接动态通道本地jar路径 **/
    private String appendLocalJarPath(String ifTypeCode){
        return payConfig.getIfStoreLocalLoadjarPath() + "xxpay-dc-" + ifTypeCode + ".jar";
    }




    ///////////////////////////////////////

    /** 调用支付函数 **/
    public AbstractRes callPaymentMethod(String ifTypeCode, PayOrder payOrder, boolean isSaveDBOrder){

        try {
            Object instance = getPaymentInterface(ifTypeCode); //获取通道对象

            PaymentInterface paymentInterface = null;  //内置通道接口
            IDCPaymentService dcPaymentService = null; //动态通道接口

            //动态通道所需参数
            ApiPayOrder apiPayOrder = null;  //订单对象
            JSONObject payParamJSON = null; //通道参数
            JSONObject sysConfigJSON = null; //系统配置项

            if(instance instanceof PaymentInterface){

                paymentInterface = (PaymentInterface)instance;

            }else{

                dcPaymentService = (IDCPaymentService)instance;

                //查询必要参数
                apiPayOrder = new ApiPayOrder();
                BeanUtils.copyProperties(payOrder, apiPayOrder);  //bean copy
                payParamJSON = getPayParamJSON(payOrder.getPassageAccountId());
                sysConfigJSON = new JSONObject();

                DBApplicationConfig dbConfig = commonService.sysConfigService.getDBApplicationConfig();
                sysConfigJSON.put("paySiteUrl", dbConfig.getPaySiteUrl());
                sysConfigJSON.put("certRootPath", payConfig.getCertRootPath());
                String notifyUrlTemplate = dbConfig.getConfig4PayNotifyUrl();
                if(!StringUtils.isBlank(ifTypeCode)) {
                    sysConfigJSON.put("notifyUrl", String.format(notifyUrlTemplate, ifTypeCode));
                }


                String returnUrlTemplate = dbConfig.getConfig4PayReturnUrl();
                if(!StringUtils.isBlank(ifTypeCode)) {
                    sysConfigJSON.put("returnUrl", String.format(returnUrlTemplate, ifTypeCode));
                }

            }

            //1. 获取orderId
            String newPayOrderId = (paymentInterface != null) ?
                    paymentInterface.getOrderId(payOrder)
                    : dcPaymentService.regenPayOrderId(apiPayOrder, payParamJSON);

            //重置orderId
            if(StringUtils.isNotEmpty(newPayOrderId)){
                payOrder.setPayOrderId(newPayOrderId);
                apiPayOrder.setPayOrderId(newPayOrderId);
            }


            //2. 重新计算下单金额
            Long newAmount = (paymentInterface != null) ?
                    paymentInterface.getAmount(payOrder)
                    : dcPaymentService.recalculatePayOrderAmount(apiPayOrder, payParamJSON);

            if(newAmount != null) {
                if(newAmount == -1) {   // 表示当前金额不可用，需更换金额重新下单

                    return ApiBuilder.bizError("当前金额不可用，需更换金额重新下单");

                }else if(newAmount > 0) {

                    payOrder.setOrderAmount(payOrder.getAmount());      // 设置客户下单金额
                    payOrder.setAmount(newAmount);                      // 设置订单金额
                    payOrder.setPaymentAmount(newAmount);               // 设置客户付款金额

                    // 重新计算订单:渠道成本费用,代理商费用,商户入账,平台利润
                    OrderCostFeeVO orderCostFeeVO = XXPayUtil.calOrderCostFeeAndIncome(newAmount, payOrder.getChannelRate(), payOrder.getAgentRate(), payOrder.getParentAgentRate(), payOrder.getMchRate(),
                            payOrder.getPayPassageRate());

                    //重新计算订单 设置订单分润数据
                    XXPayUtil.commonSetPayOrderProfit(payOrder, orderCostFeeVO);

                    // apiPayOrder 重新赋值
                    apiPayOrder.setOrderAmount(payOrder.getAmount());      // 设置客户下单金额
                    apiPayOrder.setAmount(newAmount);                      // 设置订单金额
                    apiPayOrder.setPaymentAmount(newAmount);               // 设置客户付款金额
                    apiPayOrder.setChannelCost(orderCostFeeVO.getChannelCostFee());
                    apiPayOrder.setPlatProfit(orderCostFeeVO.getPlatProfit());
                    apiPayOrder.setAgentProfit(orderCostFeeVO.getAgentProfit());
                    apiPayOrder.setParentAgentProfit(orderCostFeeVO.getParentAgentProfit());
                    apiPayOrder.setMchIncome(orderCostFeeVO.getMchIncome());
                }
            }

            //插入订单
            if(isSaveDBOrder){
                commonService.payOrderService.createPayOrder(payOrder);
            }

            // 调起支付接口 & 返回数据
            return (paymentInterface != null) ?
                    paymentInterface.pay(payOrder)
                    : convertRes( dcPaymentService.pay(apiPayOrder, payParamJSON, sysConfigJSON) );

        } catch (ServiceException e) {
            throw e;
        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            logger.error("err", e);
            throw new ApiException("调起支付通道异常");
        }

    }

}
